# -*- coding: utf-8 -*-
"""
Created on Thu Dec 14 13:07:37 2017

@author: Luther
"""
'''
继承关系:

App ---> App2 ---> App3 ---> App4
                        ---> subApp ---> subApp_sz
                                    ---> subApp_neeq
                                    ---> subApp_fund
                                    ---> subApp_bond
                                    ---> subApp_pre
                                    ---> subApp_reg

'''

#标准库:
import sys
import os
from datetime import datetime
#标准GUI库:
import tkinter as tk
from tkinter import ttk
from tkinter import scrolledtext
from tkinter.filedialog import asksaveasfilename
from tkinter.filedialog import askopenfilename
#第三方爬虫库：
import requests

#重定向stdout, stderr前备份原变量:
saveSTDOUT = sys.stdout
saveSTDERR = sys.stderr


#定义myScrolledText构造file-like Object:
class myScrolledText(scrolledtext.ScrolledText):
    def write(self, outStr):
        self.config(state='normal')
        if outStr in ["\n", ""]:
            self.insert('end', outStr)
        else:
            self.insert(
                'end',
                datetime.now().strftime("%Y-%m-%d %H:%M:%S") + " >>> " +
                outStr)
        self.config(state='disabled')
        self.see("end")
        self.master.update()


#定义基类-全站搜索App:
class App(ttk.Frame):
    #构造函数:
    def __init__(self, master=None):
        ttk.Frame.__init__(self, master)
        self.grid()
        self.MODE = tk.IntVar()
        self.MODE.set(0)
        self.PROG = tk.IntVar()
        self.PROG.set(0)
        self.MODE_DICT = {0: "false", 1: "true"}
        self.createWidgets()

    def createWidgets(self):
        self.kwLabel = ttk.Label(self, text="请输入关键词（必填）：")
        self.kwLabel.grid(row=0, sticky='e')
        self.kwEntry = ttk.Entry(self, width=50)
        self.kwEntry.grid(row=0, column=1, columnspan=2, sticky='w')
        self.sdLabel = ttk.Label(self, text="请输入开始日期(YYYY-MM-DD)：")
        self.sdLabel.grid(row=1, sticky='e')
        self.sdEntry = ttk.Entry(self, width=50)
        self.sdEntry.grid(row=1, column=1, columnspan=2, sticky='w')
        self.edLabel = ttk.Label(self, text="请输入结束日期(YYYY-MM-DD)：")
        self.edLabel.grid(row=2, sticky='e')
        self.edEntry = ttk.Entry(self, width=50)
        self.edEntry.grid(row=2, column=1, columnspan=2, sticky='w')
        self.typLabel = ttk.Label(self, text="请输入是否要全文搜索（必填）：")
        self.typLabel.grid(row=3, sticky='e')
        self.typSelectorTrue = ttk.Radiobutton(
            self, text="是", value=1, variable=self.MODE)
        self.typSelectorTrue.grid(row=3, column=1)
        self.typSelectorFalse = ttk.Radiobutton(
            self, text="否", value=0, variable=self.MODE)
        self.typSelectorFalse.grid(row=3, column=2)
        self.fileLabel = ttk.Label(self, text="请选择输出文件位置（必填）：")
        self.fileLabel.grid(row=4, sticky='e')
        self.fileSelector = ttk.Entry(self, width=50)
        self.fileSelector.grid(row=4, column=1, columnspan=2, sticky='w')
        self.browseButton = ttk.Button(self, text="浏览", command=self.browse)
        self.browseButton.grid(row=4, column=3)
        self.startButton = ttk.Button(self, text="开始检索", command=self.click)
        self.startButton.grid(row=5, columnspan=4)
        self.consoleText = myScrolledText(self)
        self.consoleText.grid(row=6, columnspan=4)
        self.consoleText.config(state='disabled')
        self.progBar = ttk.Progressbar(self, orient = "horizontal", mode = "determinate",variable=self.PROG, length = 640)
        self.progBar.grid(row=7, columnspan=4)

    '''
    #日志打印函数:
    def print(self, text):
        self.consoleText.config(state='normal')
        self.consoleText.insert(
            'end',
            datetime.now().strftime("%Y-%m-%d %H:%M:%S") + " >>> " + text +
            "\n")
        self.consoleText.config(state='disabled')
        self.consoleText.see("end")
        self.update()
    '''

    #日志导出函数:
    def text2log(self, path):
        text = self.consoleText.get("0.0", "end")
        with open(path, 'w') as f:
            f.write(text)

    #浏览按钮动作函数:
    def browse(self):
        File = asksaveasfilename(
            defaultextension='.txt', filetypes=[("CSV File", ".csv")])
        self.fileSelector.delete(0, 'end')
        self.fileSelector.insert(0, File)

    #检索按钮动作函数:
    def click(self):
        sys.stdout = self.consoleText
        sys.stderr = self.consoleText

        keyword = self.kwEntry.get()
        if keyword == "":
            raise ValueError("关键词不能为空！")
        sdate = self.sdEntry.get()
        edate = self.edEntry.get()
        outputpath = self.fileSelector.get()
        try:
            if outputpath.split(".")[-1].lower() != 'csv':
                raise ValueError
            with open(outputpath, 'w'):
                pass
        except:
            raise ValueError("输出文件设置错误！")
        isfulltext = self.MODE_DICT[self.MODE.get()]
        self.PROG.set(0)
        self.startButton.config(state="disabled")
        self.search(keyword, sdate, edate, outputpath, isfulltext)
        logpath = os.path.join(
            os.path.dirname(outputpath),
            "log{}.log".format(datetime.now().strftime("%Y%m%d%H%M%S%f")))
        print("日志文件位于：{}".format(logpath))
        self.text2log(logpath)
        self.startButton.config(state="normal")

        sys.stdout = saveSTDOUT
        sys.stderr = saveSTDERR

    def get_data(self, kw, sdate, edate, isfulltext, num):
        d = {
            'searchkey': kw,
            'sdate': sdate,
            'edate': edate,
            'isfulltext': isfulltext,
            'pageNum': num,
            'sortName': "nothing",
            'sortType': "desc",
        }
        return d

    #dict2list:
    def get_content(self, d, isfulltext):
        code = d['secCode']
        if code == None:
            code = ""
        name = d['secName']
        if name == None:
            name = ""
        title = d['announcementTitle'].replace("<em>", "").replace(
            "</em>", "").replace(",", "")
        link = "http://www.cninfo.com.cn/" + d['adjunctUrl']
        date = d['adjunctUrl'].split("/")[1]
        ret = [code, name, date, title, link]
        if isfulltext == "true":
            content = d['announcementContent']
            if content == None:
                content=""
            content = content.replace("<em>", "").replace(
                "</em>", "").replace(",", "")
            ret.append(content)
        return ret

    #主检索函数:
    def search(self, keyword, sdate, edate, outputpath, isfulltext):

        print("当前关键词：{}".format(keyword))
        print("起止日期：{} ~ {}".format(sdate, edate))
        mode_dict = {'true': '全文搜索', 'false': "标题搜索"}
        print("搜索模式：{}".format(mode_dict[isfulltext]))

        with open(outputpath, 'w') as f:
            if self.MODE.get() == 1:
                f.write("code,name,date,title,url,content\n")
            else:
                f.write("code,name,date,title,url\n")

        #获取总页数与记录数:
        i = 1
        data = self.get_data(keyword, sdate, edate, isfulltext, i)
        url = "http://www.cninfo.com.cn/cninfo-new/fulltextSearch/full"
        self.coreRequests(url, data, outputpath, isfulltext)

    def coreRequests(self, url, data, outputpath, isfulltext):
        try:
            r = requests.get(url, params=data, timeout=30)
            r.raise_for_status
        except:
            print("获取页数失败，请重试！")
        else:
            pages = r.json()['totalpages']
            totalrec = r.json()['totalAnnouncement']
            if totalrec > 0:
                print("获取总记录数成功！共{}页{}条记录".format(pages, totalrec))
                #分页检索:
                l = r.json()['announcements']
                for n in range(len(l)):
                    line = ",".join(self.get_content(l[n], isfulltext)) + "\n"
                    with open(outputpath, 'a', errors='ignore') as f:
                        f.write(line)

                self.PROG.set(int(100*(1/pages)))
                print("第1页爬取成功！")

                for x in range(2, pages + 1):
                    data['pageNum'] = x
                    try:
                        r = requests.get(url, params=data, timeout=30)
                        r.raise_for_status
                    except:
                        self.PROG.set(int(100*(x/pages)))
                        print("第{:}页爬取失败！".format(x))
                    else:
                        l = r.json()['announcements']
                        for n in range(len(l)):
                            line = ",".join(
                                self.get_content(l[n], isfulltext)) + "\n"
                            with open(outputpath, 'a', errors='ignore') as f:
                                f.write(line)
                        self.PROG.set(int(100*(x/pages)))
                        print("第{:}页爬取成功！".format(x))
                print("完成！")
                print("结果保存至：{}".format(outputpath))

            else:
                print("无相关记录！")


#定义派生类-按公司检索App2:
class App2(App):
    def __init__(self, master=None):
        ttk.Frame.__init__(self, master)
        self.grid()
        self.PROG=tk.IntVar()
        self.PROG.set(0)
        self.combotext = tk.StringVar()
        self.combotext.set("全部")
        self.category_dict = {
            '全部': '',
            '年度报告': 'category_ndbg_szsh;',
            '半年度报告': 'category_bndbg_szsh;',
            '一季度报告': 'category_yjdbg_szsh;',
            '三季度报告': 'category_sjdbg_szsh;',
            '首次公开发行及上市': 'category_scgkfx_szsh;',
            '配股': 'category_pg_szsh;',
            '增发': 'category_zf_szsh;',
            '可转换债券': 'category_kzhz_szsh;',
            '权证相关公告': 'category_qzxg_szsh;',
            '其他融资': 'category_qtrz_szsh;',
            '权益及限制出售股份': 'category_qyfpxzcs_szsh;',
            '股权变动': 'category_gqbd_szsh;',
            '交易': 'category_jy_szsh;',
            '股东大会': 'category_gddh_szsh;',
            '澄清风险业绩预告': 'category_cqfxyj_szsh;',
            '特别处理和退市': 'category_tbclts_szsh;',
            '补充及更正': 'category_bcgz_szsh;',
            '中介机构报告': 'category_zjjg_szsh;',
            '上市公司制度': 'category_ssgszd_szsh;',
            '债券公告': 'category_zqgg_szsh;',
            '其它重大事项': 'category_qtzdsx_szsh;',
            '投资者关系信息': 'category_tzzgx_szsh;',
            '董事会公告': 'category_dshgg_szsh;',
            '监事会公告': 'category_jshgg_szsh;'
        }

        self.createWidgets()

    def createWidgets(self):
        self.kwLabel = ttk.Label(self, text="请输入关键词：")
        self.kwLabel.grid(row=0, sticky='e')
        self.kwEntry = ttk.Entry(self, width=50)
        self.kwEntry.grid(row=0, column=1, sticky='w')
        self.sdLabel = ttk.Label(self, text="请输入开始日期(YYYY-MM-DD)：")
        self.sdLabel.grid(row=1, sticky='e')
        self.sdEntry = ttk.Entry(self, width=50)
        self.sdEntry.grid(row=1, column=1, sticky='w')
        self.edLabel = ttk.Label(self, text="请输入结束日期(YYYY-MM-DD)：")
        self.edLabel.grid(row=2, sticky='e')
        self.edEntry = ttk.Entry(self, width=50)
        self.edEntry.grid(row=2, column=1, sticky='w')
        self.typLabel = ttk.Label(self, text="请选择公告类别（必填）：")
        self.typLabel.grid(row=3, sticky='e')
        self.typSelector = ttk.Combobox(
            self,
            textvariable=self.combotext,
            values=list(self.category_dict.keys()),
            state='readonly')
        self.typSelector.grid(row=3, column=1, sticky='w')
        self.inputfileLabel = ttk.Label(self, text="请选择股票代码列表位置（必填）：")
        self.inputfileLabel.grid(row=4, sticky='e')
        self.inputfileSelector = ttk.Entry(self, width=50)
        self.inputfileSelector.grid(row=4, column=1, sticky='w')
        self.openButton = ttk.Button(self, text="浏览", command=self.browse_open)
        self.openButton.grid(row=4, column=2)
        self.fileLabel = ttk.Label(self, text="请选择输出文件位置（必填）：")
        self.fileLabel.grid(row=5, sticky='e')
        self.fileSelector = ttk.Entry(self, width=50)
        self.fileSelector.grid(row=5, column=1, sticky='w')
        self.saveasButton = ttk.Button(
            self, text="浏览", command=self.browse)  #继承自基类-App
        self.saveasButton.grid(row=5, column=2)
        self.startButton = ttk.Button(self, text="开始检索", command=self.click)
        self.startButton.grid(row=6, columnspan=3)
        self.consoleText = myScrolledText(self)
        self.consoleText.grid(row=7, columnspan=3)
        self.consoleText.config(state="disabled")
        self.progBar = ttk.Progressbar(self, orient = "horizontal", mode = "determinate",variable=self.PROG, length = 640)
        self.progBar.grid(row=8, columnspan=3)

    #重写click函数:
    def click(self):
        sys.stdout = self.consoleText
        sys.stderr = self.consoleText

        kw = self.kwEntry.get()
        sdate = self.sdEntry.get()
        edate = self.edEntry.get()
        if sdate != "" and edate != "":
            date = "{} ~ {}".format(sdate, edate)
        else:
            date = ""
        path_input = self.inputfileSelector.get()
        try:
            with open(path_input, 'r'):
                pass
        except:
            raise ValueError("股票代码文件错误！")
        path_output = self.fileSelector.get()
        try:
            if path_output.split(".")[-1].lower() != 'csv':
                raise ValueError
            with open(path_output, 'w'):
                pass
        except:
            raise ValueError("输出文件设置错误！")

        cat = self.category_dict[self.combotext.get()]
        self.PROG.set(0)
        self.startButton.config(state="disabled")
        self.search(path_input, path_output, cat, kw, date)

        logpath = os.path.join(
            os.path.dirname(path_output),
            "log{}.log".format(datetime.now().strftime("%Y%m%d%H%M%S%f")))
        print("日志文件位于：{}".format(logpath))
        self.text2log(logpath)
        self.startButton.config(state="normal")

        sys.stdout = saveSTDOUT
        sys.stderr = saveSTDERR

    def browse_open(self):
        File = askopenfilename()
        self.inputfileSelector.delete(0, 'end')
        self.inputfileSelector.insert(0, File)

    #生成post-data:
    def get_data(self, stock, cat, kw, num, date):
        if stock < 2000 and stock > 0:
            col = "szse_main"
        elif stock < 3000 and stock >= 2000:
            col = "szse_sme"
        elif stock < 400000 and stock >= 300000:
            col = "szse_gem"
        elif stock < 700000 and stock >= 600000:
            col = "sse"
        else:
            col = ""
        d = {
            'stock': "{:06.0f}".format(stock),
            'searchkey': kw,
            'category': cat,
            'pageNum': num,
            'pageSize': 30,
            'column': col,
            'tabName': 'fulltext',
            'sortName': "",
            'sortType': "",
            'limit': "",
            'seDate': date
        }
        return d

    #dict2list:
    def get_content(self, d):
        code = d['secCode']
        name = d['secName']
        title = d['announcementTitle'].replace(",", "")
        aj = "http://www.cninfo.com.cn/" + d['adjunctUrl']
        link = aj
        date = d['adjunctUrl'].split("/")[1]
        return [code, name, date, title, link]

    #重写主检索函数:
    def search(self, path_input, path_output, cat, kw, date):

        print("当前关键词：{}".format(kw))  #继承自基类, 后同
        print("起止日期：{}".format(date))
        print("检索类别：{}".format(self.combotext.get()))
        print("股票代码列表位置：{}".format(path_input))

        with open(path_input) as f:
            codes = f.read().split()

        with open(path_output, 'w') as f:
            f.write("code,name,date,title,url\n")

        url = "http://www.cninfo.com.cn/cninfo-new/announcement/query"

        for i in range(len(codes)):

            try:
                stock = int(codes[i])
                num = 1
                data = self.get_data(stock, cat, kw, num, date)
            
            except:
                print('股票代码"{}"不合法！'.format(codes[i]))
                continue
            
            else:

                try:
                    r = requests.post(url, data=data, timeout=30)
                    r.raise_for_status
                except:
                    print("股票代码{:06.0f}获取页数失败！".format(stock))

                else:
                    n = r.json()["totalRecordNum"]
                    if n > 0:
                        pages = n // 30 + 1
                        l = r.json()['announcements']
                        for x in range(len(l)):
                            line = ",".join(self.get_content(l[x])) + "\n"
                            with open(path_output, 'a', errors='ignore') as f:
                                f.write(line)
                        print("股票代码{:06.0f}第1页获取成功！".format(stock))

                        #获取第1页之后内容(如有):
                        if pages > 1:
                            for page in range(2, pages + 1):
                                data['pageNum'] = page
                                try:
                                    r = requests.post(url, data=data, timeout=30)
                                    r.raise_for_status
                                    l = r.json()['announcements']
                                    for x in range(len(l)):
                                        line = ",".join(self.get_content(
                                            l[x])) + "\n"
                                        with open(
                                                path_output, 'a',
                                                errors='ignore') as f:
                                            f.write(line)
                                    print("股票代码{:06.0f}第{}页获取成功！".format(
                                        stock, page))
                                except:
                                    print("股票代码{:06.0f}第{}页获取失败！".format(
                                        stock, page))

                    else:
                        print("股票代码{:06.0f}无相关记录！".format(stock))
            self.PROG.set(int(100*((i+1)/len(codes))))


        print("完成！")
        print("结果保存至：{}".format(path_output))


class App3(App2):
    def __init__(self, master=None):
        ttk.Frame.__init__(self, master)
        self.grid()
        self.PROG=tk.IntVar()
        self.PROG.set(0)
        self.createWidgets()

    def createWidgets(self):
        self.kwLabel = ttk.Label(self, text="请输入关键词（必填）：")
        self.kwLabel.grid(row=0, sticky='e')
        self.kwEntry = ttk.Entry(self, width=50)
        self.kwEntry.grid(row=0, column=1, sticky='w')
        self.sdLabel = ttk.Label(self, text="请输入开始日期(YYYY-MM-DD)：")
        self.sdLabel.grid(row=1, sticky='e')
        self.sdEntry = ttk.Entry(self, width=50)
        self.sdEntry.grid(row=1, column=1, sticky='w')
        self.edLabel = ttk.Label(self, text="请输入结束日期(YYYY-MM-DD)：")
        self.edLabel.grid(row=2, sticky='e')
        self.edEntry = ttk.Entry(self, width=50)
        self.edEntry.grid(row=2, column=1, sticky='w')
        '''
        self.typLabel = ttk.Label(self, text="请选择公告类别（必填）：")
        self.typLabel.grid(row=3, sticky='e')
        self.typSelector = ttk.Combobox(
            self,
            textvariable=self.combotext,
            values=list(self.category_dict.keys()))
        self.typSelector.grid(row=3, column=1, sticky='w')
        self.inputfileLabel = ttk.Label(self, text="请选择股票代码列表位置（必填）：")
        self.inputfileLabel.grid(row=4, sticky='e')
        self.inputfileSelector = ttk.Entry(self, width=50)
        self.inputfileSelector.grid(row=4, column=1, sticky='w')
        self.openButton = ttk.Button(self, text="浏览", command=self.browse_open)
        self.openButton.grid(row=4, column=2)
        '''
        self.fileLabel = ttk.Label(self, text="请选择输出文件位置（必填）：")
        self.fileLabel.grid(row=3, sticky='e')
        self.fileSelector = ttk.Entry(self, width=50)
        self.fileSelector.grid(row=3, column=1, sticky='w')
        self.saveasButton = ttk.Button(
            self, text="浏览", command=self.browse)  #继承自基类-App
        self.saveasButton.grid(row=3, column=2)
        self.startButton = ttk.Button(self, text="开始检索", command=self.click)
        self.startButton.grid(row=4, columnspan=3)
        self.consoleText = myScrolledText(self)
        self.consoleText.grid(row=5, columnspan=3)
        self.consoleText.config(state="disabled")
        self.progBar = ttk.Progressbar(self, orient = "horizontal", mode = "determinate",variable=self.PROG, length = 640)
        self.progBar.grid(row=6, columnspan=3)

    def click(self):
        sys.stdout = self.consoleText
        sys.stderr = self.consoleText

        kw = self.kwEntry.get()
        sdate = self.sdEntry.get()
        edate = self.edEntry.get()
        if sdate != "" and edate != "":
            date = "{} ~ {}".format(sdate, edate)
        else:
            date = ""
        '''
        path_input = self.inputfileSelector.get()
        try:
            with open(path_input,'r'):
                pass
        except:
            raise ValueError("股票代码文件错误！")
        '''
        path_output = self.fileSelector.get()
        try:
            if path_output.split(".")[-1].lower() != 'csv':
                raise ValueError
            with open(path_output, 'w'):
                pass
        except:
            raise ValueError("输出文件设置错误！")
        '''        
        try:
            cat = self.category_dict[self.combotext.get()]
        except KeyError:
            print("类别选择错误！将默认检索全部类别...")
            self.combotext.set("全部")
            cat = self.category_dict[self.combotext.get()]
        '''
        self.PROG.set(0)
        self.startButton.config(state="disabled")
        self.search(path_output, kw, date)
        self.startButton.config(state="normal")
        logpath = os.path.join(
            os.path.dirname(path_output),
            "log{}.log".format(datetime.now().strftime("%Y%m%d%H%M%S%f")))
        print("日志文件位于：{}".format(logpath))
        self.text2log(logpath)

        sys.stdout = saveSTDOUT
        sys.stderr = saveSTDERR

    def get_data(self, kw, num, date):

        d = {
            'stock': '',
            'searchkey': '{};'.format(kw),
            'plate': '',
            'category': '',
            'trade': '',
            'pageNum': num,
            'pageSize': 30,
            'column': 'neeq_company',
            'colomnTitle': '历史公告查询',
            'tabName': 'fulltext',
            'sortName': "",
            'sortType': "",
            'limit': "",
            'showTitle': '-1/searchkey/{}'.format(kw),
            'seDate': date
        }
        return d

    def search(self, path_output, kw, date):

        print("当前关键词：{}".format(kw))  #继承自基类, 后同
        print("起止日期：{}".format(date))

        with open(path_output, 'w') as f:
            f.write("code,name,date,title,url\n")

        url = "http://www.cninfo.com.cn/cninfo-new/announcement/query"

        num = 1
        data = self.get_data(kw, num, date)
        self.coreRequests(url, data, path_output)

    def coreRequests(self, url, data, path_output):
        try:
            r = requests.post(url, data=data, timeout=30)
            r.raise_for_status
        except:
            print("获取页数失败，请重试！")

        else:
            n = r.json()["totalRecordNum"]
            if n > 0:
                pages = n // 30 + 1
                print("获取总记录数成功！共{}页{}条记录".format(pages, n))

                l = r.json()['announcements']
                for x in range(len(l)):
                    line = ",".join(self.get_content(l[x])) + "\n"
                    with open(path_output, 'a', errors='ignore') as f:
                        f.write(line)
                self.PROG.set(100*(1/pages))
                print("第1页获取成功！")

                #获取第1页之后内容(如有):
                if pages > 1:
                    for page in range(2, pages + 1):
                        data['pageNum'] = page
                        try:
                            r = requests.post(url, data=data, timeout=30)
                            r.raise_for_status
                            l = r.json()['announcements']
                            for x in range(len(l)):
                                line = ",".join(self.get_content(l[x])) + "\n"
                                with open(
                                        path_output, 'a',
                                        errors='ignore') as f:
                                    f.write(line)
                            self.PROG.set(100*(page/pages))
                            print("第{}页获取成功！".format(page))
                        except:
                            self.PROG.set(100*(page/pages))
                            print("第{}页获取失败！".format(page))

                print("完成！")
                print("结果保存至：{}".format(path_output))

            else:
                print("无相关记录！")


class App4(App3):
    def __init__(self, master=None):
        ttk.Frame.__init__(self, master)
        self.grid()
        self.PROG = tk.IntVar()
        self.PROG.set(0)
        self.createWidgets()

    def createWidgets(self):
        self.modeSelectorLabel = ttk.Label(self, text="请选择板块：")
        self.modeSelectorLabel.grid(row=0, sticky='e')
        self.modeSelector = ttk.Combobox(
            self,
            values=['沪市', '深市', '新三板', '基金', '债券', '监管', '预披露'],
            state='readonly')
        self.modeSelector.current(0)
        self.modeSelector.grid(row=0, column=1, sticky='w')
        self.modeSelector.bind('<<ComboboxSelected>>', self.createSubApp)
        self.createSubApp(self)
        #self.subApp = subApp(self)
        #self.subApp.grid(row=1,columnspan=3)
        self.consoleText = myScrolledText(self)
        self.consoleText.grid(row=2, columnspan=2)
        self.consoleText.config(state="disabled")
        self.progBar = ttk.Progressbar(self, orient = "horizontal", mode = "determinate",variable=self.PROG, length = 640)
        self.progBar.grid(row=3, columnspan=2)

    def createSubApp(self, event):
        try:
            self.subApp.grid_remove()
        except:
            pass
        mode = self.modeSelector.get()
        if mode == "沪市":
            self.subApp = subApp(self)
            self.subApp.grid(row=1, columnspan=2)
        elif mode == "深市":
            self.subApp = subApp_sz(self)
            self.subApp.grid(row=1, columnspan=2)
        elif mode == "新三板":
            self.subApp = subApp_neeq(self)
            self.subApp.grid(row=1, columnspan=2)
        elif mode == "基金":
            self.subApp = subApp_fund(self)
            self.subApp.grid(row=1, columnspan=2)
        elif mode == "债券":
            self.subApp = subApp_bond(self)
            self.subApp.grid(row=1, columnspan=2)
        elif mode == "预披露":
            self.subApp = subApp_pre(self)
            self.subApp.grid(row=1, columnspan=2)
        elif mode == "监管":
            self.subApp = subApp_reg(self)
            self.subApp.grid(row=1, columnspan=2)
        else:
            pass


class subApp(App3):
    def __init__(self, master=None):
        ttk.Frame.__init__(self, master)
        self.grid()
        self.combotext = tk.StringVar()
        self.combotext.set("全部")
        self.category_dict = {
            '全部': '',
            '年报': 'category_ndbg_szsh',
            '半年报': 'category_bndbg_szsh',
            '一季报': 'category_yjdbg_szsh',
            '三季报': 'category_sjdbg_szsh',
            '业绩预告': 'category_yjygjxz_szsh',
            '权益分派': 'category_qyfpxzcs_szsh',
            '董事会': 'category_dshgg_szsh',
            '监事会': 'category_jshgg_szsh',
            '股东大会': 'category_gddh_szsh',
            '日常经营': 'category_rcjy_szsh',
            '公司治理': 'category_gszl_szsh',
            '中介报告': 'category_zj_szsh',
            '首发': 'category_sf_szsh',
            '增发': 'category_zf_szsh',
            '股权激励': 'category_gqjl_szsh',
            '配股': 'category_pg_szsh',
            '解禁': 'category_jj_szsh',
            '债券': 'category_zq_szsh',
            '其他融资': 'category_qtrz_szsh',
            '股权变动': 'category_gqbd_szsh',
            '补充更正': 'category_bcgz_szsh',
            '澄清致歉': 'category_cqdq_szsh',
            '风险提示': 'category_fxts_szsh',
            '特别处理和退市': 'category_tbclts_szsh'
        }
        self.createWidgets()

    def createWidgets(self):
        self.kwLabel = ttk.Label(self, text="请输入关键词：")
        self.kwLabel.grid(row=0, sticky='e')
        self.kwEntry = ttk.Entry(self, width=50)
        self.kwEntry.grid(row=0, column=1, sticky='w')
        self.sdLabel = ttk.Label(self, text="请输入开始日期(YYYY-MM-DD)：")
        self.sdLabel.grid(row=1, sticky='e')
        self.sdEntry = ttk.Entry(self, width=50)
        self.sdEntry.grid(row=1, column=1, sticky='w')
        self.edLabel = ttk.Label(self, text="请输入结束日期(YYYY-MM-DD)：")
        self.edLabel.grid(row=2, sticky='e')
        self.edEntry = ttk.Entry(self, width=50)
        self.edEntry.grid(row=2, column=1, sticky='w')
        self.typLabel = ttk.Label(self, text="请选择公告类别（必填）：")
        self.typLabel.grid(row=3, sticky='e')
        self.typSelector = ttk.Combobox(
            self,
            textvariable=self.combotext,
            values=list(self.category_dict.keys()),
            state='readonly')
        self.typSelector.grid(row=3, column=1, sticky='w')
        self.fileLabel = ttk.Label(self, text="请选择输出文件位置（必填）：")
        self.fileLabel.grid(row=4, sticky='e')
        self.fileSelector = ttk.Entry(self, width=50)
        self.fileSelector.grid(row=4, column=1, sticky='w')
        self.saveasButton = ttk.Button(
            self, text="浏览", command=self.browse)  #继承自基类-App
        self.saveasButton.grid(row=4, column=2)
        self.startButton = ttk.Button(self, text="开始检索", command=self.click)
        self.startButton.grid(row=5, columnspan=3)

    def get_data(self, kw, num, date, cat):

        d = {
            'stock': '',
            'searchkey': '{};'.format(kw),
            'plate': 'shmb',
            'category': '{};'.format(cat),
            'trade': '',
            'pageNum': num,
            'pageSize': 30,
            'column': 'sse',
            'tabName': 'fulltext',
            'seDate': date,
            'secid': ''
        }
        return d

    def click(self):
        sys.stdout = self.master.consoleText
        sys.stderr = self.master.consoleText

        kw = self.kwEntry.get()
        sdate = self.sdEntry.get()
        edate = self.edEntry.get()
        if sdate != "" and edate != "":
            date = "{} ~ {}".format(sdate, edate)
        else:
            date = ""

        path_output = self.fileSelector.get()
        try:
            if path_output.split(".")[-1].lower() != 'csv':
                raise ValueError
            with open(path_output, 'w'):
                pass
        except:
            raise ValueError("输出文件设置错误！")

        cat = self.category_dict[self.combotext.get()]

        self.startButton.config(state="disabled")
        self.master.PROG.set(0)
        self.search(path_output, kw, date, cat)
        self.startButton.config(state="normal")

        logpath = os.path.join(
            os.path.dirname(path_output),
            "log{}.log".format(datetime.now().strftime("%Y%m%d%H%M%S%f")))
        print("日志文件位于：{}".format(logpath))
        self.master.text2log(logpath)

        sys.stdout = saveSTDOUT
        sys.stderr = saveSTDERR

    def get_content(self, d):
        code = d['secCode']
        if code !=None:
            code = code.replace(",", "/")
        else:
            code=""
        name = d['secName']
        if name != None:
            name = name.replace(",", "/")
        else:
            name = ""
        title = d['announcementTitle'].replace(",", "")
        aj = "http://www.cninfo.com.cn/" + d['adjunctUrl']
        link = aj
        date = d['adjunctUrl'].split("/")[1]
        return [code, name, date, title, link]

    def search(self, path_output, kw, date, cat):

        print("当前关键词：{}".format(kw))  #继承自基类, 后同
        print("起止日期：{}".format(date))
        print("检索类别：{}".format(self.combotext.get()))

        with open(path_output, 'w') as f:
            f.write("code,name,date,title,url\n")

        url = "http://www.cninfo.com.cn/new/hisAnnouncement/query"

        num = 1
        data = self.get_data(kw, num, date, cat)

        self.coreRequests(url, data, path_output)

    def coreRequests(self, url, data, path_output):
        try:
            r = requests.post(url, data=data, timeout=30)
            r.raise_for_status
        except:
            print("获取页数失败，请重试！")

        else:
            n = r.json()["totalRecordNum"]
            if n > 0:
                pages = n // 30 + 1
                print("获取总记录数成功！共{}页{}条记录".format(pages, n))

                l = r.json()['announcements']
                for x in range(len(l)):
                    line = ",".join(self.get_content(l[x])) + "\n"
                    with open(path_output, 'a', errors='ignore') as f:
                        f.write(line)
                self.master.PROG.set(100*(1/pages))
                print("第1页获取成功！")

                #获取第1页之后内容(如有):
                if pages > 1:
                    for page in range(2, pages + 1):
                        data['pageNum'] = page
                        try:
                            r = requests.post(url, data=data, timeout=30)
                            r.raise_for_status
                            l = r.json()['announcements']
                            for x in range(len(l)):
                                line = ",".join(self.get_content(l[x])) + "\n"
                                with open(
                                        path_output, 'a',
                                        errors='ignore') as f:
                                    f.write(line)
                            self.master.PROG.set(100*(page/pages))
                            print("第{}页获取成功！".format(page))
                        except:
                            self.master.PROG.set(100*(page/pages))
                            print("第{}页获取失败！".format(page))

                print("完成！")
                print("结果保存至：{}".format(path_output))

            else:
                print("无相关记录！")

class subApp_sz(subApp):
    def get_data(self, kw, num, date, cat):

        d = {
            'stock': '',
            'searchkey': '{};'.format(kw),
            'plate': 'sz',
            'category': '{};'.format(cat),
            'trade': '',
            'pageNum': num,
            'pageSize': 30,
            'column': 'szse',
            'tabName': 'fulltext',
            'seDate': date,
            'secid': ''
        }
        return d


class subApp_neeq(subApp):
    def __init__(self, master=None):
        ttk.Frame.__init__(self, master)
        self.grid()
        self.combotext = tk.StringVar()
        self.combotext.set("全部")
        self.category_dict = {
            '全部': '',
            '临时公告': 'category_lsgg_gfzr',
            '定期公告': 'category_dqgg_gfzr',
            '中介机构公告': 'category_zjjg_gfzr',
            '持续信息披露': 'category_cxpl_gfzr',
            '首次信息披露': 'category_scpl_gfzr'
        }
        self.createWidgets()

    def get_data(self, kw, num, date, cat):

        d = {
            'stock': '',
            'searchkey': '{};'.format(kw),
            'plate': '',
            'category': '{};'.format(cat),
            'trade': '',
            'pageNum': num,
            'pageSize': 30,
            'column': 'neeq_company',
            'tabName': 'fulltext',
            'seDate': date,
            'secid': ''
        }
        return d


class subApp_fund(subApp):
    def __init__(self, master=None):
        ttk.Frame.__init__(self, master)
        self.grid()
        self.combotext = tk.StringVar()
        self.combotext.set("全部")
        self.category_dict = {
            '全部': '',
            '招募设立': 'category_jjzm_jjgg',
            '说明书更新': 'category_smsgx_jjgg',
            '年报': 'category_ndbg_jjgg',
            '中报': 'category_bndbg_jjgg',
            '季报': 'category_jdbg_jjgg',
            '净值': 'category_jzgg_jjgg',
            '投资组合': 'category_zhtz_jjgg',
            '申购赎回': 'category_sgsh_jjgg',
            '基金费率': 'category_jjfl_jjgg',
            '销售渠道': 'category_xsqd_jjgg',
            '分红': 'category_fh_jjgg',
            '高管及基金经理': 'category_ggjjjl_jjgg',
            '持有人大会': 'category_fecyr_jjgg',
            '基本信息变更': 'category_jbxxbg_jjgg',
            '其他': 'category_qt_jjgg'
        }
        self.createWidgets()

    def get_data(self, kw, num, date, cat):

        d = {
            'stock': '',
            'searchkey': '{};'.format(kw),
            'plate': '',
            'category': '{};'.format(cat),
            'trade': '',
            'pageNum': num,
            'pageSize': 30,
            'column': 'fund',
            'tabName': 'fulltext',
            'seDate': date,
            'secid': ''
        }
        return d


class subApp_bond(subApp):
    def __init__(self, master=None):
        ttk.Frame.__init__(self, master)
        self.grid()
        self.combotext = tk.StringVar()
        self.combotext.set("全部")
        self.category_dict = {
            '全部': '',
            '债券发行上市': 'category_zqfxss_zqgg',
            '债券定期公告': 'category_zqdq_zqgg',
            '债券付息公告': 'category_zqfx_zqgg',
            '债券到期兑付停止交易公告': 'category_zqdqdftz_zqgg',
            '债券其他公告': 'category_zqqt_zqgg',
            '可转债发行': 'category_kzzfx_zqgg',
            '可转债转股': 'category_kzzzg_zqgg',
            '可转债回售': 'category_kzzhs_zqgg',
            '可转债赎回': 'category_kzzsh_zqgg',
            '可转债付息': 'category_kzzfxi_zqgg',
            '可转债其他': 'category_kzzqt_zqgg'
        }
        self.createWidgets()

    def get_data(self, kw, num, date, cat):

        d = {
            'stock': '',
            'searchkey': '{};'.format(kw),
            'plate': '',
            'category': '{};'.format(cat),
            'trade': '',
            'pageNum': num,
            'pageSize': 30,
            'column': 'bond',
            'tabName': 'fulltext',
            'seDate': date,
            'secid': ''
        }
        return d


class subApp_pre(subApp):
    def __init__(self, master=None):
        ttk.Frame.__init__(self, master)
        self.grid()
        self.createWidgets()

    def createWidgets(self):
        self.kwLabel = ttk.Label(self, text="请输入关键词：")
        self.kwLabel.grid(row=0, sticky='e')
        self.kwEntry = ttk.Entry(self, width=50)
        self.kwEntry.grid(row=0, column=1, sticky='w')
        self.sdLabel = ttk.Label(self, text="请输入开始日期(YYYY-MM-DD)：")
        self.sdLabel.grid(row=1, sticky='e')
        self.sdEntry = ttk.Entry(self, width=50)
        self.sdEntry.grid(row=1, column=1, sticky='w')
        self.edLabel = ttk.Label(self, text="请输入结束日期(YYYY-MM-DD)：")
        self.edLabel.grid(row=2, sticky='e')
        self.edEntry = ttk.Entry(self, width=50)
        self.edEntry.grid(row=2, column=1, sticky='w')
        self.fileLabel = ttk.Label(self, text="请选择输出文件位置（必填）：")
        self.fileLabel.grid(row=3, sticky='e')
        self.fileSelector = ttk.Entry(self, width=50)
        self.fileSelector.grid(row=3, column=1, sticky='w')
        self.saveasButton = ttk.Button(
            self, text="浏览", command=self.browse)  #继承自基类-App
        self.saveasButton.grid(row=3, column=2)
        self.startButton = ttk.Button(self, text="开始检索", command=self.click)
        self.startButton.grid(row=4, columnspan=3)

    def get_data(self, kw, num, date):

        d = {
            'stock': '',
            'searchkey': '{};'.format(kw),
            'plate': '',
            'category': '',
            'trade': '',
            'pageNum': num,
            'pageSize': 30,
            'column': 'pre_disclosure',
            'tabName': 'fulltext',
            'seDate': date,
            'secid': ''
        }
        return d

    def click(self):
        sys.stdout = self.master.consoleText
        sys.stderr = self.master.consoleText

        kw = self.kwEntry.get()
        sdate = self.sdEntry.get()
        edate = self.edEntry.get()
        if sdate != "" and edate != "":
            date = "{} ~ {}".format(sdate, edate)
        else:
            date = ""

        path_output = self.fileSelector.get()
        try:
            if path_output.split(".")[-1].lower() != 'csv':
                raise ValueError
            with open(path_output, 'w'):
                pass
        except:
            raise ValueError("输出文件设置错误！")
        self.master.PROG.set(0)
        self.startButton.config(state="disabled")
        self.search(path_output, kw, date)
        self.startButton.config(state="normal")

        logpath = os.path.join(
            os.path.dirname(path_output),
            "log{}.log".format(datetime.now().strftime("%Y%m%d%H%M%S%f")))
        print("日志文件位于：{}".format(logpath))
        self.master.text2log(logpath)

        sys.stdout = saveSTDOUT
        sys.stderr = saveSTDERR

    def get_content(self, d):
        title = d['announcementTitle'].replace(",", "")
        aj = "http://www.cninfo.com.cn/" + d['adjunctUrl']
        link = aj
        date = d['adjunctUrl'].split("/")[1]
        return [date, title, link]

    def search(self, path_output, kw, date):

        print("当前关键词：{}".format(kw))  #继承自基类, 后同
        print("起止日期：{}".format(date))

        with open(path_output, 'w') as f:
            f.write("date,title,url\n")

        url = "http://www.cninfo.com.cn/new/hisAnnouncement/query"

        num = 1
        data = self.get_data(kw, num, date)

        self.coreRequests(url, data, path_output)


class subApp_reg(subApp):
    def __init__(self, master=None):
        ttk.Frame.__init__(self, master)
        self.grid()
        self.combotext = tk.StringVar()
        self.combotext.set("全部")
        self.category_dict = {
            '全部': '',
            '停复牌': 'category_jgjg_tfp',
            '业务通知': 'category_jgjg_ywtz',
            '批评处罚及公开谴责': 'category_jgjg_ppcfgkqz',
            '交易风险提示': 'category_jgjg_jyfxts',
            '其他': 'category_jgjg_qt'
        }
        self.combotext2 = tk.StringVar()
        self.combotext2.set("全部")
        self.category_dict2 = {
            '全部': '',
            '深交所': 'jgjg_sz',
            '上交所': 'jgjg_sh',
            '结算公司': 'jgjg_jsgs',
            '证监会': 'jgjg_zjh'
        }
        self.createWidgets()

    def createWidgets(self):
        self.kwLabel = ttk.Label(self, text="请输入关键词：")
        self.kwLabel.grid(row=0, sticky='e')
        self.kwEntry = ttk.Entry(self, width=50)
        self.kwEntry.grid(row=0, column=1, sticky='w')
        self.sdLabel = ttk.Label(self, text="请输入开始日期(YYYY-MM-DD)：")
        self.sdLabel.grid(row=1, sticky='e')
        self.sdEntry = ttk.Entry(self, width=50)
        self.sdEntry.grid(row=1, column=1, sticky='w')
        self.edLabel = ttk.Label(self, text="请输入结束日期(YYYY-MM-DD)：")
        self.edLabel.grid(row=2, sticky='e')
        self.edEntry = ttk.Entry(self, width=50)
        self.edEntry.grid(row=2, column=1, sticky='w')
        self.typLabel = ttk.Label(self, text="请选择公告类别（必填）：")
        self.typLabel.grid(row=3, sticky='e')
        self.typSelector = ttk.Combobox(
            self,
            textvariable=self.combotext,
            values=list(self.category_dict.keys()),
            state='readonly')
        self.typSelector.grid(row=3, column=1, sticky='w')
        self.typLabel2 = ttk.Label(self, text="请选择发文机构（必填）：")
        self.typLabel2.grid(row=4, sticky='e')
        self.typSelector2 = ttk.Combobox(
            self,
            textvariable=self.combotext2,
            values=list(self.category_dict2.keys()),
            state='readonly')
        self.typSelector2.grid(row=4, column=1, sticky='w')
        self.fileLabel = ttk.Label(self, text="请选择输出文件位置（必填）：")
        self.fileLabel.grid(row=5, sticky='e')
        self.fileSelector = ttk.Entry(self, width=50)
        self.fileSelector.grid(row=5, column=1, sticky='w')
        self.saveasButton = ttk.Button(
            self, text="浏览", command=self.browse)  #继承自基类-App
        self.saveasButton.grid(row=5, column=2)
        self.startButton = ttk.Button(self, text="开始检索", command=self.click)
        self.startButton.grid(row=6, columnspan=3)

    def get_data(self, kw, num, date, cat, reg):

        d = {
            'stock': '',
            'searchkey': '{};'.format(kw),
            'plate': reg,
            'category': '{};'.format(cat),
            'trade': '',
            'pageNum': num,
            'pageSize': 30,
            'column': 'regulator',
            'tabName': 'fulltext',
            'seDate': date,
            'secid': ''
        }
        return d

    def click(self):
        sys.stdout = self.master.consoleText
        sys.stderr = self.master.consoleText

        kw = self.kwEntry.get()
        sdate = self.sdEntry.get()
        edate = self.edEntry.get()
        if sdate != "" and edate != "":
            date = "{} ~ {}".format(sdate, edate)
        else:
            date = ""

        path_output = self.fileSelector.get()
        try:
            if path_output.split(".")[-1].lower() != 'csv':
                raise ValueError
            with open(path_output, 'w'):
                pass
        except:
            raise ValueError("输出文件设置错误！")
        cat = self.category_dict[self.combotext.get()]
        reg = self.category_dict2[self.combotext2.get()]
        self.master.PROG.set(0)
        self.startButton.config(state="disabled")
        self.search(path_output, kw, date, cat, reg)
        self.startButton.config(state="normal")
        logpath = os.path.join(
            os.path.dirname(path_output),
            "log{}.log".format(datetime.now().strftime("%Y%m%d%H%M%S%f")))
        print("日志文件位于：{}".format(logpath))
        self.master.text2log(logpath)

        sys.stdout = saveSTDOUT
        sys.stderr = saveSTDERR

    def search(self, path_output, kw, date, cat, reg):

        print("当前关键词：{}".format(kw))  #继承自基类, 后同
        print("起止日期：{}".format(date))
        print("检索类别：{}".format(self.combotext.get()))
        print("发文机构：{}".format(self.combotext2.get()))

        with open(path_output, 'w') as f:
            f.write("code,name,date,title,url\n")

        url = "http://www.cninfo.com.cn/new/hisAnnouncement/query"

        num = 1
        data = self.get_data(kw, num, date, cat, reg)

        self.coreRequests(url, data, path_output)


if __name__ == "__main__":
    top = tk.Tk()  #主窗体
    nb = ttk.Notebook(top)  #选项卡容器
    app = App(nb)
    #app2 = App2(nb)
    #app3 = App3(nb)
    app4 = App4(nb)
    nb.add(app, text="全站检索")
    #nb.add(app2, text="按公司检索")
    #nb.add(app3, text="新三板数据检索")
    nb.add(app4, text='分类检索')
    nb.grid(row = 0)
    cr = ttk.Label(top, text="Copyright © 2018 LutherBu 财会瞭望哨")
    cr.grid(row = 1)
    top.title("巨潮公告获取程序v2.5(20181007)")
    top.mainloop()
